package com.ajieee.merge.util;

import com.ajieee.merge.entity.finance.Quote;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * 技术指标计算
 *
 * @author ajieee
 * @date 2025-11-02
 */
public class TechIndicator {

	/**
	 * 简单移动平均线
	 *
	 * @param quotes 行情数据
	 * @param period 指标周期
	 * @return key:日期  value:MA
	 */
	public static Map<LocalDate, BigDecimal> ma(List<? extends Quote> quotes, int period) {
		Map<LocalDate, BigDecimal> ma = new LinkedHashMap<>();
		BigDecimal sum = BigDecimal.ZERO;
		for (int i = 0; i < quotes.size(); i++) {
			sum = sum.add(BigDecimal.valueOf(quotes.get(i).getClose()));

			if (i < period - 1) {
				continue;
			}
			if (i - period >= 0) {
				sum = sum.subtract(BigDecimal.valueOf(quotes.get(i - period).getClose()));
			}
			ma.put(quotes.get(i).getDataDate(), sum.divide(BigDecimal.valueOf(period), 8, RoundingMode.HALF_UP));
		}
		return ma;
	}

	/**
	 * 指数移动平均线
	 *
	 * @param quotes 行情数据
	 * @param period 指标周期
	 * @return key:日期  value:EMA
	 */
	public static Map<LocalDate, BigDecimal> ema(List<? extends Quote> quotes, int period) {
		Map<LocalDate, BigDecimal> map = new LinkedHashMap<>();

		// 平滑系数 k = 2 / (N + 1)
		BigDecimal kFactor = BigDecimal.TWO.divide(BigDecimal.valueOf(period + 1), 8, RoundingMode.HALF_UP);
		BigDecimal oneMinusFactor = BigDecimal.ONE.subtract(kFactor);

		// 第一个EMA值使用收盘价替代
		BigDecimal price = BigDecimal.valueOf(quotes.getFirst().getClose());
		BigDecimal preEma = price;
		map.put(quotes.getFirst().getDataDate(), price);
		for (int i = 1; i < quotes.size(); i++) {
			// EMA = price * k + preEma * (1 - k)
			price = BigDecimal.valueOf(quotes.get(i).getClose());
			BigDecimal ema = price.multiply(kFactor).add(preEma.multiply(oneMinusFactor));
			preEma = ema;
			map.put(quotes.get(i).getDataDate(), ema);
		}

		return map;
	}

	/**
	 * 计算MACD指标
	 *
	 * @param quotes     行情数据
	 * @param diffPeriod 短期周期
	 * @param deaPeriod  长期周期
	 * @param macdPeriod
	 * @return key:日期  value:EMA
	 */
	public static Map<LocalDate, BigDecimal> macd(List<? extends Quote> quotes, int diffPeriod, int deaPeriod, int macdPeriod) {
		Map<LocalDate, BigDecimal> emaDiff = ema(quotes, diffPeriod);
		Map<LocalDate, BigDecimal> emaDea = ema(quotes, deaPeriod);

		Map<LocalDate, BigDecimal> difMap = new LinkedHashMap<>();
		Map<LocalDate, BigDecimal> deaMap = new LinkedHashMap<>();
		Map<LocalDate, BigDecimal> macdMap = new LinkedHashMap<>();
		BigDecimal preDea = BigDecimal.ZERO;
		for (Quote quote : quotes) {
			LocalDate date = quote.getDataDate();
			// dif = emaDiff - emaDea
			BigDecimal dif = emaDiff.get(date).subtract(emaDea.get(date));
			difMap.put(date, dif);

			// dea = dif * k + preDea * (1 - k)
			BigDecimal kFactor = BigDecimal.TWO.divide(BigDecimal.valueOf(macdPeriod + 1), 8, RoundingMode.HALF_UP);
			BigDecimal oneMinusFactor = BigDecimal.ONE.subtract(kFactor);
			BigDecimal dea = dif.multiply(kFactor).add(preDea.multiply(oneMinusFactor));
			deaMap.put(date, dea);

			// macd = (dif - dea) * 2
			BigDecimal macd = dif.subtract(dea).multiply(BigDecimal.TWO);
			macdMap.put(date, macd);

			preDea = dea;
		}

		return macdMap;
	}

}
